
#include "uart_pkg.h"

/**
  * @brief  串口发送字节
  * @param  uartx 串口字节数组接收结构体
  * @param  byte  发送的字节数据
  * @retval NULL
  * @note   使用该函数前要保证有串口发送字节函数的原型，并在定义串口发送数据结构体时将对应函数指针正确地指向串口发送字节函数的原型
  *         在已经有串口发送字节函数的情况下可以不使用该函数，编写该函数仅仅是为了框架更加完善
  */
void uart_send_byte(uart_send_t *uartx, uint8_t byte)
{
    uartx->send_byte(byte);
}

/**
  * @brief  串口发送数组(带包头包尾)
  * @param  uartx 串口字节数组接收结构体
  * @param  arr   发送的数组
  * @param  len   发送的数组的长度
  * @param  check 是否发送校验和(0:不发送，1:发送)
  * @retval NULL
  * @note   该函数在一般的数组发送函数的基础上加入了包头包尾的发送
  *         在已经有串口发送数组函数的情况下可以不使用该函数，编写该函数仅仅是为了框架更加完善
  */
void uart_send_array(uart_send_t *uartx, uint8_t *arr, uint8_t len, uint8_t check)
{
    // 发送包头
    for (uint8_t i = 0; i < uartx->send_head_len; i++) uartx->send_byte(uartx->send_head[i]);
    
    // 发送数据
    if (!check) // 无校验和
    {
        for (uint8_t i = 0; i < len; i++) uartx->send_byte(arr[i]);
    }
    else    // 有校验和
    {
        for (uint8_t i = 0; i < len; i++)
        {
            uartx->send_byte(arr[i]);
            uartx->checksum += arr[i];
        }
        uartx->checksum &= 0xFF;
        uartx->send_byte((uint8_t)uartx->checksum);
    }
    
    // 发送包尾
    for (uint8_t i = 0; i < uartx->send_tail_len; i++) uartx->send_byte(uartx->send_tail[i]);
}

/**
  * @brief  串口发送字符串
  * @param  uartx 串口字节数组接收结构体
  * @param  str   发送的字符串
  * @retval NULL
  * @note   在已经有串口发送字符串函数的情况下不用使用该函数，编写该函数仅仅是为了框架更加完善
  */
void uart_send_string(uart_send_t *uartx, char *str)
{
    while (*str) uartx->send_byte(*str++);
}

/**
  * @brief  串口接收数据(使用指定长度置接收标志位)
  * @param  uartx 串口字节数组接收结构体
  * @param  data  单次接收的串口数据
  * @retval NULL
  * @note   同一个串口同时只能使用一种数据接收方式，使用此函数时不需要调用串口接收超时判断函数
  */
void uart_data_receive(uart_data_t *uartx, uint8_t data)
{
    if (!uartx->rec_flag)
    {
        if (!uartx->rec_start) memset(uartx->rec_data, 0, sizeof(uartx->rec_data)); // 开始新一轮接收前清空数据
        uartx->rec_start = 1;   // 说明已经开始接收串口数据
        
        uartx->rec_data[uartx->rec_cnt++] = data;
        if (uartx->rec_cnt >= UART_REC_DATA_SIZE) uartx->rec_cnt = 0;   // 避免接收溢出
        
        if (uartx->rec_cnt >= uartx->rec_len)
        {
            uartx->rec_start = 0;
            uartx->rec_cnt = 0;
            uartx->rec_flag = 1;
        }
    }
}

/**
  * @brief  清除串口接收完成标志位
  * @param  uartx 串口字节数组接收结构体
  * @retval NULL
  * @note   NULL
  */
void uart_data_rec_clearflag(uart_data_t *uartx)
{
    uartx->rec_flag = 0;
}

/**
  * @brief  串口接收字节数组(使用包头包尾置接收标志位)
  * @param  uartx 串口字节数组接收结构体
  * @param  data  单次接收的串口数据
  * @retval NULL
  * @note   同一个串口同时只能使用一种数据接收方式，使用此函数时可以选择调用对应的串口接收超时判断函数，
  *         此处的超时判断仅仅用来清空字节数组避免下次正常接收时出错，不会置相应标志位，所以如果不使用超时判断，
  *         发送数据给单片机时每次一定确保包头包尾正确，不然会导致下一次接收数据出错
  */
void uart_arr_receive(uart_arr_t *uartx, uint8_t data)
{
    if (!uartx->rec_flag)
    {
        if (!uartx->rec_start) memset(uartx->rec_arr, 0, sizeof(uartx->rec_arr));   // 开始新一轮接收前清空串口字节数组
        uartx->rec_start = 1;   // 说明已经开始接收串口数据
        uartx->rec_tmr = 0;     // 接收一次串口数据计时重置
        
        switch (uartx->rec_state)
        {
            case 0: {   // 接收并判断包头
                uartx->rec_head_buf[uartx->rec_head_cnt++] = data;
                
                if (uartx->rec_head_cnt >= uartx->rec_head_len)
                {
                    if (!strncmp((const char *)uartx->rec_head, (const char *)uartx->rec_head_buf, uartx->rec_head_len))
                    {
                        uartx->rec_state = 1;
                        memset(uartx->rec_head_buf, 0, sizeof(uartx->rec_head_buf));    // 清空包头缓存
                        uartx->rec_head_cnt = 0;                                        // 清零包头接收计次
                    }
                    else
                    {
                        for (uint8_t i = 1; i < uartx->rec_head_len; i++) uartx->rec_head_buf[i-1] = uartx->rec_head_buf[i];
                        uartx->rec_head_buf[uartx->rec_head_len-1] = 0;
                        uartx->rec_head_cnt--;  // 包尾接收计次减1
                    }
                }
            } break;
            case 1: {   // 接收数据并判断包尾
                uartx->rec_arr[uartx->rec_cnt++] = data;    // 将串口数据依次存入对应的串口字节数组
                uartx->rec_tail_buf[uartx->rec_tail_cnt++] = data;
                if (uartx->rec_tail_cnt >= UART_REC_DATA_SIZE) uartx->rec_tail_cnt = 0; // 避免接收溢出
                
                if (uartx->rec_tail_cnt >= uartx->rec_tail_len)
                {
                    if (!strncmp((const char *)uartx->rec_tail, (const char *)uartx->rec_tail_buf, uartx->rec_tail_len))
                    {
                        memset(uartx->rec_tail_buf, 0, sizeof(uartx->rec_tail_buf));    // 清空包头缓存
                        uartx->rec_tail_cnt = 0;                                        // 清零包头接收计次
                        
                        uartx->rec_arr_len = uartx->rec_cnt - 1;    // 获取字节数组的实际长度
                        for (uint8_t i = 1; i <= uartx->rec_tail_len; i++) uartx->rec_arr[uartx->rec_cnt-i] = 0;    // 去除接收字节数组中的包尾
                        uartx->rec_cnt = 0;
                        uartx->rec_flag = 1;
                        uartx->rec_state = 0;
                    }
                    else
                    {
                        for (uint8_t i = 1; i < uartx->rec_tail_len; i++) uartx->rec_tail_buf[i-1] = uartx->rec_tail_buf[i];
                        uartx->rec_tail_buf[uartx->rec_tail_len-1] = 0;
                        uartx->rec_tail_cnt--;  // 包尾接收计次减1
                    }
                }
            } break;
            default: break;
        }
    }
}

/**
  * @brief  串口接收超时判断(使用包头包尾置接收标志位)
  * @param  uartx 串口字节数组接收结构体
  * @retval NULL
  * @note   NULL
  */
void uart_arr_rec_timeout_judge(uart_arr_t *uartx)
{
    if (uartx->rec_start)   // 若已经开始接收串口数据
    {
        if (++uartx->rec_tmr >= UART_TIMEOUT)   // 判断接收数据的空闲时间
        {
            uartx->rec_tmr = 0;
            uartx->rec_start = 0;

            uartx->rec_state = 0;                                           // 清零串口接收状态
            memset(uartx->rec_arr, 0, sizeof(uartx->rec_arr));              // 清空串口字符数组
            uartx->rec_cnt = 0;                                             // 清零串口接收计次
            memset(uartx->rec_head_buf, 0, sizeof(uartx->rec_head_buf));    // 清空包头缓存
            uartx->rec_head_cnt = 0;                                        // 清零包头接收计次
            memset(uartx->rec_tail_buf, 0, sizeof(uartx->rec_tail_buf));    // 清空包尾缓存
            uartx->rec_tail_cnt = 0;                                        // 清零包尾接收计次
        }
    }
    else uartx->rec_tmr = 0;
}

/**
  * @brief  清除串口接收完成标志位
  * @param  uartx 串口字节数组接收结构体
  * @retval NULL
  * @note   NULL
  */
void uart_arr_rec_clearflag(uart_arr_t *uartx)
{
    uartx->rec_flag = 0;
}

/**
  * @brief  串口接收字符串(使用回车换行符置接收标志位)
  * @param  uartx 串口字符串接收结构体
  * @param  data  单次接收的串口数据
  * @retval NULL
  * @note   同一个串口同时只能使用一种数据接收方式，使用此函数时可以选择调用对应的串口接收超时判断函数，
  *         此处的超时判断仅仅用来清空字符串避免下次正常接收时出错，不会置相应标志位，所以如果不使用超时判断，
  *         发送数据给单片机时每次一定要用回车换行符"\r\n"标志字符串结束，不然会导致下一次接收数据出错
  */
void uart_str_ln_receive(uart_str_t *uartx, uint8_t data)
{
    if (!uartx->rec_flag)   // 若还未开始接收或者已经处理完接收的数据则开始新一轮接收
    {
        if (!uartx->rec_start) memset(uartx->rec_str, '\0', sizeof(uartx->rec_str));    // 开始新一轮接收前清空串口字符数组
        uartx->rec_start = 1;   // 说明已经开始接收串口数据
        uartx->rec_tmr = 0;     // 接收一次串口数据计时重置
        
        // 将串口数据依次存入对应的串口字符数组
        uartx->rec_str[uartx->rec_cnt++] = data;
        
        // 判断字符串结束条件
        if (uartx->rec_str[uartx->rec_cnt-1] == '\r' || uartx->rec_str[uartx->rec_cnt-1] == '\n')
        {
            uartx->rec_str[uartx->rec_cnt-1] = '\0';        // 添加字符串结束符
            uartx->rec_cnt = 0;                             // 清零串口接收计次
            uartx->rec_str_len = strlen(uartx->rec_str);    // 获取字符串的实际长度
            uartx->rec_flag = 1;                            // 串口接收完成标志位置位
        }
        
        // 避免串口接收溢出
        if (uartx->rec_cnt >= UART_REC_DATA_SIZE)
        {
            memset(uartx->rec_str, '\0', sizeof(uartx->rec_str));   // 接收溢出后清空串口字符数组
            uartx->rec_cnt = 0;                                     // 清零串口接收计次，重新从头开始接收
        }
    }
}

/**
  * @brief  串口接收超时判断(使用回车换行符置接收标志位)
  * @param  uartx 串口字符串接收结构体
  * @retval NULL
  * @note   需要在以1ms为周期的服务函数中调用，如1ms执行一次的定时中断服务函数
  */
void uart_str_ln_rec_timeout_judge(uart_str_t *uartx)
{
    if (uartx->rec_start)   // 若已经开始接收串口数据
    {
        if (++uartx->rec_tmr >= UART_TIMEOUT)   // 判断接收数据的空闲时间
        {
            uartx->rec_tmr = 0;
            uartx->rec_start = 0;

            memset(uartx->rec_str, '\0', sizeof(uartx->rec_str));   // 清空串口字符数组
            uartx->rec_cnt = 0;                                     // 清零串口接收计次
        }
    }
    else uartx->rec_tmr = 0;
}

/**
  * @brief  清除串口接收完成标志位(使用回车换行符置接收标志位)
  * @param  uartx 串口字符串接收结构体
  * @retval NULL
  * @note   NULL
  */
void uart_str_ln_rec_clearflag(uart_str_t *uartx)
{
    uartx->rec_flag = 0;
}

/**
  * @brief  串口接收字符串(使用超时检测置接收标志位)
  * @param  uartx 串口字符串接收结构体
  * @param  data  单次接收的串口数据
  * @retval NULL
  * @note   同一个串口同时只能使用一种数据接收方式，使用此函数时需要调用串口接收超时判断函数
  */
void uart_str_receive(uart_str_t *uartx, uint8_t data)
{
    if (!uartx->rec_flag)   // 若还未开始接收或者已经处理完接收的数据则开始新一轮接收
    {
        if (!uartx->rec_start) memset(uartx->rec_str, '\0', sizeof(uartx->rec_str));    // 开始新一轮接收前清空串口字符数组
        uartx->rec_start = 1;   // 说明已经开始接收串口数据
        uartx->rec_tmr = 0;     // 接收一次串口数据计时重置
        
        // 将串口数据依次存入对应的串口字符数组
        uartx->rec_str[uartx->rec_cnt++] = data;
        
        // 避免串口接收溢出
        if (uartx->rec_cnt >= UART_REC_DATA_SIZE)
        {
            memset(uartx->rec_str, '\0', sizeof(uartx->rec_str));   // 清空串口字符数组
            uartx->rec_cnt = 0;                                     // 清零串口接收计次
        }
    }
}

/**
  * @brief  串口接收超时判断
  * @param  uartx 串口字符串接收结构体
  * @retval NULL
  * @note   需要在以1ms为周期的服务函数中调用，如1ms执行一次的定时中断服务函数
  */
void uart_str_rec_timeout_judge(uart_str_t *uartx)
{
    if (uartx->rec_start)   // 若已经开始接收串口数据
    {
        if (++uartx->rec_tmr >= UART_TIMEOUT)   // 判断接收数据的空闲时间
        {
            uartx->rec_tmr = 0;
            uartx->rec_start = 0;

            uartx->rec_str[uartx->rec_cnt] = '\0';          // 添加字符串结束符
            uartx->rec_cnt = 0;                             // 清零串口接收计次
            uartx->rec_str_len = strlen(uartx->rec_str);    // 获取字符串的实际长度
            uartx->rec_flag = 1;                            // 串口接收完成标志位置位
        }
    }
    else uartx->rec_tmr = 0;
}

/**
  * @brief  清除串口接收完成标志位
  * @param  uartx 串口字符串接收结构体
  * @retval NULL
  * @note   NULL
  */
void uart_str_rec_clearflag(uart_str_t *uartx)
{
    uartx->rec_flag = 0;
}
